/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <string.h>
#include <front/text.h>
#include <front/mem.h>
#include <front/parse_support.h>
#include "fe_flags.h"
#include "expressions.h"
#include "cfrontPrint.h"
#include "strings.h"
#include "type.h"
#include "init.h"
#include "eval.h"
#include "typing.h"

#define LEFT_ASSOC 1

static struct s_void_cfront_TraverseTable set_ld_st_expr_actions;
static struct s_cfront_TraverseTable link_expr_to_statement_actions;
static struct s_cfront_TraverseTable addr_taken_actions;
static struct s_cfront_TraverseTable sizeof_actions;
static struct s_cfront_TraverseTable set_const_actions;

int escape_double = TRUE;

expression
Create_IntExpr (SrcInfo loc, int v)
{
  NumExpr r;
  char *num = Front_StrAllocf ("%d",v);
  
  r = Create_NumExpr (loc, num);
  NumExpr_val(r) = fe_malloc (sizeof (mpz_t));
  mpz_init_set_si (*NumExpr_val(r), v);
  return r;
}

expression Create_binexpr (SrcInfo loc, List_expression l, List_operator ops)
{
  expression e = List_expression_data(l);
  
#if LEFT_ASSOC
  for (l = List_expression_next(l); l;
       l = List_expression_next(l), ops = List_operator_next(ops)) {
    e = Create_bin_expression (loc, e, List_operator_data(ops), List_expression_data(l));
  }
  return e;
#else  
  if (ops == NULL) return e;
  return Create_bin_expression (loc, e,
            List_operator_data(ops),
	    Create_binexpr(loc,
              List_expression_data(l)(l),
              List_operator_next(ops)));
#endif
}

expression List_expression_last (List_expression l)
{
  for (; l; l = List_expression_next(l)) {
    if (List_expression_next(l)==NULL) return List_expression_data(l);
  }
  return NULL;
}


/**********/

Bool address_taken (expression e)
{
  declarator d;
  
  if (e==NULL) return FALSE;
  if (expression_adr_taken(e)) return TRUE;
  if (expression_tag(e) != IdExpr_kind) return FALSE;

  d = IdExpr_decl (e);
  if (d == NULL) return FALSE;
  return declarator_tag(d) == Id_Decl_kind && Id_Decl_address_taken(d);
}

Bool expr_address_taken (expression e)
{
  if (!e) return FALSE;
  return expression_adr_taken(e);
}

/**********/

Int type_generation = 0;

static void make_static (expression e)
{
  List_expression l;
  
  if (e==NULL) return;
  set_expression_is_static(e);
  switch (expression_tag(e)) {
    case CompoundInit_kind:
      for (l = CompoundInit_inits(e); l; l = List_expression_next(l)) {
        make_static (List_expression_data(l));
      }
      break;
    case LabeledInit_kind:
      make_static (LabeledInit_init(e));
      break;
    default:
      break;
  }
}

/**************/

/*
 * Returns type of IdExpr or IndexExpr. Returns NULL for other expression types. @@@Pointers?
 */
Type get_expression_type(expression e)
{
  declarator      decl; 
  expression      exp;
  
  if (expression_type(e) != NULL) return expression_type(e);
  
  switch(expression_tag(e)) {
  case IdExpr_kind:
    decl = IdExpr_decl(e);
    return decl == NULL ? NULL : declarator_type(decl);
    break;
  case IndexExpr_kind:
    exp  = IndexExpr_array(e);
    decl = IdExpr_decl(exp);
    return (decl == NULL || Type_tag(declarator_type(decl)) != ArrayType_kind)  
      ?  NULL :  ArrayType_element(declarator_type(decl));
    break;
  case ParExpr_kind:
    return get_expression_type(ParExpr_expr(e));
    break;
  default:
    return NULL;
  }
  return NULL; 
  
}

/* Load store setting */


static void set_load (expression e, Bool ld)
{
  if (ld) set_expression_is_load(e);
  else reset_expression_is_load(e);
  if (ld) reset_expression_is_store(e);
  switch (expression_tag(e)) {
    case ParExpr_kind:
      set_load (ParExpr_expr(e), ld);
      break;
    case MemberExpr_kind:
      set_load (MemberExpr_aggr(e), FALSE);
      break;
    case IndexExpr_kind: 
      set_load (IndexExpr_array(e), FALSE);
    default:
      break;
  }
  return;
}

static void set_store (expression e, Bool st, Bool ld)
{
  if (st) set_expression_is_store(e);
  else reset_expression_is_store(e);
  if (st && !ld) reset_expression_is_load(e);
  switch (expression_tag(e)) {
    case ParExpr_kind:
      set_store (ParExpr_expr(e), st, ld);
      break;
    case MemberExpr_kind:
      set_load (MemberExpr_aggr(e), FALSE);
      break;
    case IndexExpr_kind: 
      set_load (IndexExpr_array(e), FALSE);
    default:
      break;
  }
  return;
}

static void Action_set_ld_st_expression (expression e)
{
  Type t;
  if (e == NULL) return;
  t = expression_type(e);
  
  switch (expression_tag(e)) {
    case DerefMemberExpr_kind:
      set_load (e, TRUE);
      break;
    case IndexExpr_kind: 
      set_load (e, TRUE);
      break;
    case IdExpr_kind:
      if (is_array_type(t)) break;
      set_load (e, TRUE);
      break;
    case MemberExpr_kind:
      if (!expression_is_load(MemberExpr_aggr(e))) break;
      set_load (e, TRUE);
      break;
    case mon_expression_kind: 
      switch (mon_expression_op(e)) {
        case DerefOp: 
	  set_load (e, TRUE);
	  break;
        case AddrOp: 
	  set_load (mon_expression_arg(e), FALSE);
	  break;
        case PreInc: 
        case PreDec: 
        case PostInc: 
        case PostDec: 
	  set_load (mon_expression_arg(e), TRUE);
	  set_store (mon_expression_arg(e), TRUE, TRUE);
	  break;
        default: break;
      }
      break;
    case bin_expression_kind:
      if (!Is_assigning(e)) return;
      set_store (bin_expression_lhs(e), TRUE, Ass != bin_expression_op(e));
      break;
    default:
      break;
  }
}

/************/

expression Copy_expr (expression e)
{
  expression a;
  a = fe_malloc (sizeof (*a)); 
  *a = *e;
  return a;
}


static void
decorate_expr (expression x)
{
  Scope sc = expression_old_scope(x);
  if (sc==NULL) sc = expression_scope(x);
  ast_decorate (expression, x,
                expression_cur_func(x), expression_cur_statement(x), sc);
}

static void
set_props (expression x, expression t)
{
  expression_cur_func(x)     = expression_cur_func(t);
  expression_cur_statement(x) = expression_cur_statement(t);
  expression_flags(x)        = expression_flags(t);
  expression_scope(x)        = expression_scope(t);
  expression_hook(x)         = expression_hook(t);
  expression_init_id_decl(x) = expression_init_id_decl(t);
  expression_type(x)         = expression_type(t);
}

/*
   Deep_copy_expr, Deep_copy_exprs
   -------------------------------
   It is the responsibility of the caller of these functions to (re)decorate
   the obtained copies if needed.
*/

expression
Deep_copy_expr (expression e)
{
  expression c;
  
  if (e==NULL) return NULL;
  
  c = Traverse_copy_expression (e, &cfront_copy_actions);
  set_props (c, e);
  return c;
}

List_expression
Deep_copy_exprs (List_expression l)
{
  List_expression m = Traverse_copy_List_expression (l, &cfront_copy_actions);
  List_expression n;
  
  for (n = m; n !=NULL; n = List_expression_next(n), l = List_expression_next(l)) {
    expression c = List_expression_data(n);
    expression e = List_expression_data(l);
    set_props (c, e);  
  }
  return m;
}

/* x = y */
void
replace_expr (expression x, expression y, int i)
{
  expression t = NULL;

  NOT_USED(i);

  t = Create_NumExpr(no_srcinfo, "");
  *t = *x;
  *x = *y;
  
  set_props (x, t);

  decorate_expr (x);
}

void
replace_exprs (List_expression x, List_expression y, int i)
{
  List_expression l, m;
  
  NOT_USED(i);
  
  for (l = x, m = y; l && m; l = TAIL(l), m = TAIL(m)) {
#warning Todo: why not call set_props?
    expression_req_type(HEAD(m)) = expression_req_type (HEAD(l));
  }
  
  *x = *y;
  
  for (l = x; l; l = TAIL(l)) {
    decorate_expr(HEAD(l));
  }
}

/************/

expression is_compiler_generated (expression e)
{
  List_expression l;
  
  if (e==NULL || expression_tag(e) != CallExpr_kind || expression_loc(e) >= 0) return NULL;
  /* Return second argument, if available */
  l = CallExpr_args (e);
  if (l == NULL) return NULL;
  if (List_expression_next(l) == NULL) return List_expression_data(l);
  l = List_expression_next(l);
  return List_expression_data(l);
}

Bool Is_assigning_op (operator op)
{
  return (Ass <= (int)op && op <= AssXor)||
         (op == PAssAdd || op == PAssSub);
}

Bool Is_assigning (expression e)
{
  operator op;

  if (e == NULL) return FALSE;
  if (expression_tag(e) != bin_expression_kind) return FALSE;
  op = bin_expression_op(e);
  return Is_assigning_op(op);
}

Bool Is_deref (expression e)
{
  switch (expression_tag(e)) {
    case DerefMemberExpr_kind:
    case IndexExpr_kind: 
      return TRUE;
    case ParExpr_kind:
      return Is_deref (ParExpr_expr(e));
    case MemberExpr_kind:
      return Is_deref (MemberExpr_aggr(e));
    case mon_expression_kind: return mon_expression_op(e) == DerefOp;
    case CallExpr_kind:
      { expression a = is_compiler_generated (e);
        if (a != NULL) return Is_deref (a);
      }
      break;
    default:
      break;
  }
  return FALSE;
}

Bool Is_enum (expression e)
{
  declarator d;
  
  if (e == NULL || expression_tag(e) != IdExpr_kind) return FALSE;
  
  d = IdExpr_decl(e);
  if (d==NULL) return FALSE;
  return declarator_tag(d)== enumerator_kind;
}

#if 0
Bool is_funcdef (expression func)
{
  declarator d;
/*   Type t = Norm_type(expression_type(func), expression_scope(func)); */
  Type t = expression_type (func);
  
  if (expression_tag(func) != IdExpr_kind) return FALSE;
  
  d = IdExpr_decl (func);
  if (d == NULL) return FALSE; /* ? */
  if (t != NULL && is_function_type (t)) return TRUE; /* Only global func decls */
  if (!is_global(d)) return FALSE;
  
  /* Investigate func further */
  return TRUE;
}
#endif

/************/

/* Address taken stuff */

static expression Action_addr_taken_expression (expression e, expression addr)
{
  declarator d;

  if (e==NULL) return NULL;

  if (addr != NULL) set_expression_adr_taken(e);

  switch (expression_tag(e)) {
    case mon_expression_kind:
      if (mon_expression_op(e) == AddrOp) return e;
      break;
    case ParExpr_kind:
      return addr;
    case IdExpr_kind:
      if (addr == NULL) break;
      set_expression_adr_taken(e);
      d = IdExpr_decl(e);
      if (d==NULL || declarator_tag(d) != Id_Decl_kind) break;
      Id_Decl_address_taken(d) = TRUE;
      break;
    case SizeofExpr_kind:
      for (e = SizeofExpr_expr(e); expression_tag(e) == ParExpr_kind; e = ParExpr_expr(e)) {
        set_expression_sizeof_arg (e);
      }
      set_expression_sizeof_arg (e);
      break;
    default:
      break;
  }
  return NULL;
}

static expression Action_addr_taken_attr_par (attr_pars e, expression addr)
{
  List_expression l;
  
  NOT_USED(addr);
  if (e==NULL) return NULL;

  for (l = attr_pars_exprs(e); l; l = List_expression_next(l)) {
    set_expression_sizeof_arg (List_expression_data(l));
  }
  return NULL;
}


/******* sizeof argument functions *****/

static Bool Action_pre_SizeofExpr (expression e, Bool so)
{ NOT_USED(e); NOT_USED(so);
  return TRUE;
}

static Bool Action_sizeof_expression (expression e, Bool so)
{ 
  if (e==NULL || !so) return so;
  set_expression_sizeof_arg (e);
  return so;
}

/************/

static statement Action_Pre_set_statement(statement s, statement cur)
{
  if (s == NULL) return cur;

  statement_previous(s) = cur;
  return s;
}

static statement Action_link_expression_to_statement(expression e, statement cur)
{
  if (e != NULL) expression_cur_statement(e) = cur;
  return cur;
}


/************/

static Bool Action_set_const_declarator (declarator d, Bool c)
{ if (d==NULL) return c;
  switch (declarator_tag(d)) {
    case DeclStruct_kind: return TRUE;   /* Bit field */
    case NoDeclStruct_kind: return TRUE; /* Bit field */
    case ArrayDecl_kind: return TRUE;    /* Array size */
    default: break;
  }
  return c;
}

static Bool Action_set_const_statement (statement s, Bool c)
{
  if (s==NULL) return c;
  
  if (statement_tag(s)==CaseStat_kind) return TRUE;
  
  return FALSE;
}

static Bool Action_set_const_expression (expression e, Bool c)
{
  if (e==NULL) return c;
  
  if (c) set_expression_must_be_const(e);
  
  return c;
}


/************/

static void init_traversals (void)
{
  static Bool done = FALSE;
  
  if (done) return;
  done = TRUE;

  /* Mark loads and stores */
  set_ld_st_expr_actions.action_expression[POST_ACTION] = Action_set_ld_st_expression;
  
  /* Mark expression that must be constant */
  set_const_actions.traversal = Traverse_Down;
  set_const_actions.action_declarator[PRE_ACTION] = (void*)Action_set_const_declarator;
  set_const_actions.action_statement[PRE_ACTION] = (void*)Action_set_const_statement;
  set_const_actions.action_expression[PRE_ACTION] = (void*)Action_set_const_expression;
  
  /* Set address taken */
  addr_taken_actions.traversal = Traverse_Down;
  addr_taken_actions.action_expression[PRE_ACTION] = (void*)Action_addr_taken_expression;
  addr_taken_actions.action_attr_pars[PRE_ACTION] = (void*)Action_addr_taken_attr_par;
  
  /* Sizeof arguments taken */
  sizeof_actions.traversal = Traverse_Down;
  sizeof_actions.action_SizeofExpr[PRE_ACTION] = (void*)Action_pre_SizeofExpr;
  sizeof_actions.action_expression[PRE_ACTION] = (void*)Action_sizeof_expression;

  /* Link expressions to statements */
  link_expr_to_statement_actions.traversal = Traverse_Down;
  link_expr_to_statement_actions.action_statement[PRE_ACTION]  = (void*)Action_Pre_set_statement;
  link_expr_to_statement_actions.action_expression[PRE_ACTION] = (void*)Action_link_expression_to_statement;

}


expression
Parse_Type_expression (Type root_type, Scope sc, char *s, int n, ...)
{
  va_list p;
  expression result;
  
  va_start(p, n);
  Put_sub_trees (n, p);
  va_end(p);
  result = Parse_expression (s, 0);

  expression_scope(result) = sc;
  expression_req_type(result) = root_type;
  decorate_expr(result);
  return result;
}

void
Parse_replace_expression (expression e, char *s, int n, ...)
{
  va_list p;
  expression result;
  Type root_type = expression_type (e);
  
  va_start(p, n);
  Put_sub_trees (n, p);
  va_end(p);
  result = Parse_expression (s, 0);

  expression_type(result) = root_type;
  replace_expr (e, result, 0);
}

void ast_set_ld_st (void *node, 
                    void  (*trav_void) (void *me, Trav_void_table table),
                    void *(*trav)      (void *me, Trav_table table, void *val)
		   )
{
  init_traversals ();
  trav_void (node, &set_ld_st_expr_actions);
  trav (node, &set_const_actions, FALSE);
}

void ast_set_addr_taken (void *node, void (*trav_void) (), void *(*trav) ())
{
  init_traversals ();
  trav (node, &addr_taken_actions, NULL);
  NOT_USED (trav_void);
}

expression declare_temp (Type t, Scope scope, SrcInfo si, expression init)
{
  declaration decl;
  expression e1;
  statement stat;
  
  if (!t) t = int_type; /* Undeclared function, build-in functions */
  else t = unqualified_type (t);
  
  { int num = 0;
    if (init) num = expression__num(init);
    t = defined_type(t, num, scope);
  }
  
  scope = Statement_Scope(scope, Type_scope (t)==NULL); /* The declarator may contain local types */
  if (scope==NULL) return NULL;
  stat = compound_statementScope_data(scope);
  
  e1 = Make_temp_expression (t, init, &decl, scope);
  ast_propagate_srcinfo (declaration, decl, si);
  ast_propagate_srcinfo (expression, e1, si);
  
  if (init) {
    FRONT_APPEND_NODE(compound_statement_locdecls(stat), decl);
  } else {
    FRONT_PREPEND_NODE(decl, compound_statement_locdecls(stat));
  }
  
  return e1;
}

static expression
MyCOPY_expression(expression me, expression me2)
{
  NOT_USED(me2);
  if (me && expression_tag(me)==StringExpr_kind) return Copy_StringExpr(me);
  return NULL;
}

expression replace_by_temp (expression e)
{
  Type t = expression_type(e);
  Scope scope = expression_scope(e);
  SrcInfo si = expression_src_info(e);
#if 0
  expression e1 = declare_temp (pointer_to_type (t), scope, si, NULL);
#else
  expression e1 = declare_temp (pointer_promote(t), scope, si, NULL);
#endif
  
/*   set_original (e1, e); */
  return e1;
}

expression
Replace_by_temp (expression e)
{
  expression e1 = replace_by_temp(e);
  Parse_replace_expression (e,
       "%unary_expression = (%assignment_expression), %assignment_expression", 
       3, e1, Deep_copy_expr(e), Deep_copy_expr(e1));
  return e1;
}

expression
Replace_by_temp_pointer (expression e)
{
  Type t = expression_type(e);
  Scope sc = expression_scope(e);
  expression adr;
  expression e1, e2;
  
  if (is_array_type (t)) {
    t = pointer_to_type (dereference_type(t));
    adr = Parse_Type_expression (t, sc, "&(%expression)", 1, Deep_copy_expr(e));
    expression_type(adr) = t;

    expression_scope(adr) = sc;

    e1 = Replace_by_temp (adr);
    Parse_replace_expression (e, "(%cast_expression)", 1, adr);
  } else {
    t = pointer_to_type (t);
    adr = Parse_Type_expression (t, sc, "&(%expression)", 1, Deep_copy_expr(e));

    expression_scope(adr) = sc;

    e1 = Replace_by_temp (adr);
    e2 = comma_expression_last(adr);
    Parse_replace_expression (e2, "(* %cast_expression)", 1, Deep_copy_expr(e2));
    replace_expr (e, adr, 0);
  }
  return e1;
}

/* expression get_original (expression e) */
/* {   */
/*   if (e==NULL) return e; */
/*   if (expression_original(e) != NULL) return expression_original(e); */
/*   return e; */
/* } */

static Bool has_side_effect (expression e, Bool s)
{ 
  if (s) return TRUE;
  switch (expression_tag(e)) {
    case bin_expression_kind:
      return Is_assigning(e);
    case mon_expression_kind:
      return PreInc <= mon_expression_op(e) && mon_expression_op(e) <= PostDec;
    default:
      break;
  }
  return TRUE; 
}

Bool Has_side_effect (expression e)
{ static struct s_cfront_TraverseTable side_effects;
  side_effects.action_CallExpr      [PRE_ACTION] = (void*)has_side_effect;
  side_effects.action_bin_expression[PRE_ACTION] = (void*)has_side_effect;
  side_effects.action_mon_expression[PRE_ACTION] = (void*)has_side_effect;
  
  return (Bool)Traverse_expression (e, &side_effects, FALSE);
}

void ast_link_exprs_to_stats (void *node, 
                   void *(*trav) (void *me, Trav_table table, void *val),
		   statement parent)
{
  init_traversals ();
  trav (node, &link_expr_to_statement_actions, parent);
}

void Link_exprs_to_stats(statement parent)
{
  ast_link_exprs_to_stats (cfront_root, (void*)Traverse_cfront, parent);
}

Bool is_temp_id (expression e)
{ switch (expression_tag(e)) {
    case IdExpr_kind:
      return strncmp (IDENT_STRING(IdExpr_id_id(e)), "__temp_", 7) == 0;
    default:
      break;
  }
  return FALSE;
}

declarator get_temp (expression e)
{ Ident id;
  declarator d;
  Type t;
  
  switch (expression_tag(e)) {
    case IdExpr_kind:
      id = IdExpr_id_id(e);
      d = IdExpr_decl(e);
      t = expression_type(e);
      
      if (d != NULL && is_temp_id(e)) return d;
      break;
    case ParExpr_kind:
      return get_temp (ParExpr_expr(e));
    case comma_expression_kind:
      return get_temp (comma_expression_last(e));
    default:
      break;
  }
  return NULL;
}

Bool is_temp (expression e)
{ Ident id;
  declarator d;
  Type t;
  
  if (is_temp_id(e)) return TRUE;
  
  switch (expression_tag(e)) {
    case IdExpr_kind:
      id = IdExpr_id_id(e);
      d = IdExpr_decl(e);
      t = expression_type(e);
      
      if (d == NULL) return FALSE;
      if (is_global(d) || is_static(d)) return FALSE;
      /* TODO: check for address taken */
      return is_scalar_type(t) || is_function_type(t);
    case ParExpr_kind:
      return is_temp (ParExpr_expr(e));
    case comma_expression_kind:
      return is_temp (comma_expression_last(e));
    default:
      break;
  }
  return FALSE;
}

static Bool is_lvalue (declarator d, Type t)
{
  if (d==NULL) return FALSE;
  if (is_function_type(t)) return FALSE; /* Only global func decls */
  switch (declarator_tag(d)) {
    case enumerator_kind:
      return FALSE;
    default:
      break;
  }
  return TRUE;
}

Bool Is_lvalue (expression e)
{
  Type t;
  
  if (e==NULL) return FALSE;

  t = expression_type (e);
  switch (expression_tag(e)) {
    case mon_expression_kind:
      switch (mon_expression_op(e)) {
        case DerefOp: return TRUE;
	default:
	  break;
      }
      break;
    case IndexExpr_kind: return TRUE;
    case MemberExpr_kind: return Is_lvalue (MemberExpr_aggr(e));
    case DerefMemberExpr_kind: return TRUE;
    case ParExpr_kind: return Is_lvalue (ParExpr_expr(e));
    case IdExpr_kind: return is_lvalue (IdExpr_decl(e), t);
    case StringExpr_kind: return TRUE;
    default:
      break;
  }
  return FALSE;
}

/* ----------------------------------------------------------------------------
 * The textual representation of E with a pre- and postfix
 */
char *
expression_text(char *pre, expression e, char *post)
{
  char buffer[100000];
  FILE *f = print_file;
  
  open_print_string (buffer, TRUE);
  Print_String(pre);
  Print_expression(e);
  Print_String(post);
  close_print_string ();
  print_file = f;
  return fe_strdup(buffer);
}

void Print_expr (expression e)
{ Print_expression (e);
  printf ("\n");
}

void MyPRINT_expression(expression e)
{
  if (e==NULL) return;
      
  switch (expression_tag(e)) {
    case StringExpr_kind: 
      advance_position (expression_loc(e));
      Print_String (""); /* Goto string position */
      if (printing_to_string && escape_double)  Print_StringExpr(e,"\\\""); 
      else                                      Print_StringExpr(e,"\""); 
      return;
    case CharExpr_kind:
      advance_position (expression_loc(e));
      Print_String (""); /* Goto string position */
      Print_CharExpr(e,"'"); 
      return;
    case WideCharExpr_kind:
      advance_position (expression_loc(e));
      Print_String (""); /* Goto string position */
      Print_WideCharExpr(e,"'"); 
      return;
    case mon_expression_kind: 
      switch (mon_expression_op(e)) {
        case PostInc:
        case PostDec:
          PRINT_expression (mon_expression_arg(e));
          Print_monadic_operator (mon_expression_op(e));
          return;
        default: break;
      }
      break;
/*     case StatExpr_kind: */
/*       if (expression_loc(e) > 0) break; */
/*       PRINT_statement (StatExpr_stat(e)); */
/*       return; */
    default: break;
  }
 
  Print_expression (e);
}

void init_expr_tables (void)
{ /* Overwrite print action */
  cfront_print_actions.action_expression[PRE_ACTION] = MyPRINT_expression;
  /* Overwrite retrieve action action */
/*   cfront_applied_actions.action_IdExpr[PRE_ACTION] = retrieve_idexpr; */
  /* Overwrite copy action */
  cfront_copy_actions.action_expression[PRE_ACTION] = MyCOPY_expression;
}

static Bool _expr_is_constant (expression e, enum constant_expr_type tp);
static Bool exprs_are_constant (List_expression es);

#warning The functions to test if expressions are constant need a serious overhaul
static Bool mon_expr_is_constant(mon_expression e, enum constant_expr_type tp)
{
  switch( mon_expression_op(e) ) {
    case AddrOp:
      if ( tp == constant_expr ) return TRUE;
      else return FALSE;
    case PosOp:
    case NegOp:
    case InvOp:
    case NotOp:
      return _expr_is_constant(mon_expression_arg(e), tp);
    case DerefOp:
      return expr_is_address_constant(mon_expression_arg(e));
    default:
      return FALSE; 
  }       
}

static Bool bin_expr_is_constant(bin_expression e, enum constant_expr_type tp)
{
  switch(bin_expression_op(e)) {
    case CorOp:
    case CandOp:
    case OrOp:
    case XorOp:
    case AndOp:
    case EqOp:
    case NeOp:
    case LtOp:
    case GtOp:
    case LeOp:
    case GeOp:
    case ShlOp:
    case ShrOp:
    case AddOp:
    case SubOp:
    case MulOp:
    case DivOp:
    case ModOp:
    case PAddOp:
    case PSubOp:
    case PPSubOp:
      return _expr_is_constant (bin_expression_lhs(e), tp) &&
             _expr_is_constant (bin_expression_rhs(e), tp);
    default:
      return FALSE;
  }
}

static Bool _expr_is_constant (expression e, enum constant_expr_type tp)
{  
  List_expression exprs;
  switch (expression_tag(e)) {
    case TypeExpr_kind:
    case NumExpr_kind:      
    case CharExpr_kind:
    case StringExpr_kind:    
    case WideCharExpr_kind:  
    case SizeofExpr_kind:            
    case SizeofType_kind:   
    case LabelExpr_kind: 
      return TRUE;     
    case FloatExpr_kind:
      if ( tp == integral_constant ) return FALSE; 
      else return TRUE; 
    case bin_expression_kind:
      return bin_expr_is_constant(e, tp);
    case mon_expression_kind:
      return mon_expr_is_constant(e, tp);      
    case CondExpr_kind:
      return _expr_is_constant (CondExpr_cond(e), tp) &&
             _expr_is_constant (CondExpr_thn(e), tp) &&
             _expr_is_constant (CondExpr_els(e), tp);
    case CastExpr_kind: 
      if ( tp == integral_constant && 
           !is_integral_type(expression_type(e)) )
        return FALSE;
      if ( tp == arithmetic_constant &&
           !is_arithmetic_type(expression_type(e)) )
        return FALSE;
      return _expr_is_constant (CastExpr_expr(e), tp);        
    case ParExpr_kind:
      return _expr_is_constant (ParExpr_expr(e), tp);    
    case LabeledInit_kind:
      return _expr_is_constant (LabeledInit_init(e), tp);
    case CompoundInit_kind:
      for(exprs = CompoundInit_inits(e); exprs; exprs = TAIL(exprs))
        if ( !_expr_is_constant(HEAD(exprs), tp) )
          return FALSE;
      return TRUE;    
    case CompoundLit_kind:
      return _expr_is_constant (CompoundLit_expr(e), tp);   
    case IdExpr_kind:  
      if (Is_enum(e)) return TRUE;
      break;  
    case comma_expression_kind:
      if (!exprs_are_constant (comma_expression_pre (e))) return FALSE;
      return expr_is_constant (comma_expression_last (e));
    case StatExpr_kind:
    case IndexExpr_kind:
    case CallExpr_kind:
    case MemberExpr_kind:
    case DerefMemberExpr_kind:
      return FALSE;
  }
  return FALSE;  
} 

/* C standard definition 3.4 Constant Expressions */

Bool expr_is_constant (expression e)
{ return _expr_is_constant(e, constant_expr); }

Bool expr_is_integral_constant (expression e)
{ return _expr_is_constant(e, integral_constant); }

Bool expr_is_arithmetic_constant (expression e)
{ return _expr_is_constant(e, arithmetic_constant); }

Bool expr_is_null_pointer_constant (expression e)
{
  if (!e) return FALSE;
  switch (expression_tag (e)) {
    case CastExpr_kind:         return is_pointer_type(expression_type (e)) && /* We should check for void* type (3.2.2.3) but we don't. */
                                       expr_is_null_pointer_constant (CastExpr_expr (e));
    case ParExpr_kind:          return expr_is_null_pointer_constant (ParExpr_expr (e));
    case comma_expression_kind: return expr_is_null_pointer_constant (comma_expression_last (e)) && exprs_are_constant (comma_expression_pre (e));
    default:                    return expr_is_arithmetic_constant (e) && is_null_pointer (e);
  }
}

#if 0

#warning TODO : check if operand of AddrOp is lvalue with static storage
Bool expr_is_object_address (expression e)
{
  Type t = expression_type(e);
  if ( is_array_type(t) && expression_tag(e) != CompoundInit_kind )
    return TRUE;
  if ( expression_tag(e) == mon_expression_kind &&
       mon_expression_op(e) == AddrOp &&
       is_object_type(expression_type(mon_expression_arg(e)))  
     )
    return TRUE;
  if ( expression_tag(e) == CastExpr_kind &&
       is_pointer_type(t) && 
       (expr_is_arithmetic_constant (CastExpr_expr(e)) || expr_is_address_constant (CastExpr_expr(e)))
     )
    return TRUE;
  return FALSE;
}

/* can an -> operator be used inside an expression inside an & operand
   while having to be evaluated to calculate the resulting address? 
   I cannot think of a situation, but it is explicitly mentioned in the C standard.

   Yup: &(x[i]->a), or even: &(f()->a)
*/

Bool expr_is_address_constant (expression e)
{
  /* C99: or integer cast to pointer type */
  return expr_is_object_address (e) || 
         is_function_type(expression_type(e));        
}

Bool expr_is_object_address_pm_integral (expression e)
{
  if ( expr_is_object_address (e) )
    return TRUE;
  if ( expression_tag(e) != bin_expression_kind ||
       ( bin_expression_op(e) != AddOp && bin_expression_op(e) != SubOp) 
     ) return FALSE;
  if ( ( expr_is_integral_constant(bin_expression_lhs(e)) && 
         expr_is_object_address_pm_integral(bin_expression_rhs(e)) ) ||
       ( expr_is_integral_constant(bin_expression_rhs(e)) && 
         expr_is_object_address_pm_integral(bin_expression_lhs(e)) )
     ) return TRUE;
  return FALSE;
}

#else

static expression take_array (expression e)
{
  Type t;
  expression a;
  expression b;
  switch (expression_tag (e)) {
    case IndexExpr_kind:
      a = IndexExpr_array (e);
      b = IndexExpr_index (e);
      break;
    case bin_expression_kind:
      a = bin_expression_lhs (e);
      b = bin_expression_rhs (e);
      break;
    default: ;
  }
  t = expression_type (a);
  return is_pointer_type (t) || is_array_type (t) ? a : b;
}

static expression take_index (expression e)
{
  Type t;
  expression a;
  expression b;
  switch (expression_tag (e)) {
    case IndexExpr_kind:
      a = IndexExpr_array (e);
      b = IndexExpr_index (e);
      break;
    case bin_expression_kind:
      a = bin_expression_lhs (e);
      b = bin_expression_rhs (e);
      break;
    default: ;
  }
  t = expression_type (a);
  return is_pointer_type (t) || is_array_type (t) ? b : a;
}

static Bool expr_is_constant_object (expression e)
{
  if (!e) return FALSE;
  switch (expression_tag (e)) {
    case IdExpr_kind:
      return TRUE; /* Unless it's an enum constant, how do we know? */

    case mon_expression_kind:
      return mon_expression_op (e) == DerefOp && expr_is_address_constant (mon_expression_arg (e));

    case ParExpr_kind:
      return expr_is_constant_object (ParExpr_expr (e));

    case comma_expression_kind:
      return exprs_are_constant (comma_expression_pre (e)) &&
             expr_is_constant_object (comma_expression_last (e));

    case IndexExpr_kind:
      return (expr_is_constant_object (take_array (e)) || expr_is_address_constant (take_array (e))) && expr_is_constant (take_index (e));

    case MemberExpr_kind:
      return expr_is_constant_object (MemberExpr_aggr (e));

    case DerefMemberExpr_kind:
      return expr_is_address_constant (DerefMemberExpr_aggr (e));

    default:
 return FALSE;
  }
}

Bool expr_is_address_constant (expression e)
{
  if (!e) return FALSE;
  switch (expression_tag (e)) {
    case IdExpr_kind:
      return is_array_type (expression_type (e)) || is_function_type (expression_type (e));

    case mon_expression_kind:
      if (mon_expression_op (e) == AddrOp) return expr_is_constant_object (mon_expression_arg (e));
      if (mon_expression_op (e) == DerefOp) return expr_is_address_constant (mon_expression_arg (e));
      return FALSE;

    case bin_expression_kind:
      switch (bin_expression_op (e)) {
        case AddOp:
        case SubOp:
        case PAddOp:
        case PSubOp:
          return expr_is_address_constant (take_array (e)) && expr_is_constant (take_index (e));

        default:
          return FALSE;
      }

    case CastExpr_kind:
      return (is_pointer_type(expression_type (e)) &&
              is_pointer_type(expression_type (CastExpr_expr (e))) &&
              expr_is_address_constant (CastExpr_expr (e)))
             ||
             (is_pointer_type(expression_type (e)) &&
              expr_is_arithmetic_constant (CastExpr_expr (e))) /* regression/tests/stdlib.dir/mallocr.c uses '(char*)-1' */
             ||
             (is_pointer_type(expression_type (e)) &&
              is_array_type(expression_type (CastExpr_expr (e))))
             ||
             (is_pointer_type(expression_type (e)) &&
              is_function_type(expression_type (CastExpr_expr (e))));

    case ParExpr_kind:
      return expr_is_address_constant (ParExpr_expr (e));

    case comma_expression_kind:
      return exprs_are_constant (comma_expression_pre (e)) &&
             expr_is_address_constant (comma_expression_last (e));

    default: return FALSE;
  }
}

#endif

static Bool exprs_are_constant (List_expression es)
{
  if (!es) return TRUE; /* End recursion. */
  return expr_is_constant (List_expression_data (es)) && exprs_are_constant (List_expression_next (es));
}

Bool expr_is_constant_initializer (expression e)
{
  /* C standard says: initializer is constant if:
    - an arithmetic constant expression,
    - a null pointer constant,
    - an address constant, or
    - an address constant for an object type plus or minus an integral constant expression
  */
  if (expression_tag(e) == CompoundInit_kind) {
    List_expression l;
    for (l = CompoundInit_inits(e); l; l = TAIL(l)) {
      if (!expr_is_constant_initializer(HEAD(l))) return FALSE;
    }
    return TRUE;
  }
  
  if ( expr_is_arithmetic_constant(e) ||
       expr_is_null_pointer_constant(e) ||
       expr_is_address_constant(e) /* ||
       expr_is_object_address_pm_integral(e) */)
    return TRUE; 
  return FALSE;
}

Bool
is_null_pointer(expression e)
{
  AP_Integer v = EvalIntExpr (e);
  if (!v) return FALSE;
  return mpz_sgn (*v)==0;
}

Bool
expr_in_register(expression e)
{
  declarator d;
  
  if (expression_tag (e) != IdExpr_kind) return FALSE;
  d = IdExpr_decl (e);
  return declarator_storage_class (d) == Register;
}

Bool
is_object_in_memory (IdExpr expr)
{
  return IdExpr_decl (expr) && is_decl_in_memory (IdExpr_decl (expr));
/*   if (!is_object_type(expression_type(expr))) return FALSE; */
/*   return has_static_storage (IdExpr_decl (expr)) || address_taken(expr); */
}

Bool
is_function_pointer_call(expression me)
{
  expression e = me;
  if (expression_tag(e) == CallExpr_kind) {
    e = CallExpr_func (e);
  }
  if (expression_tag(e) != IdExpr_kind) return TRUE;
  declarator d = IdExpr_decl(e);
  if (!d) return FALSE;
  Type t = expanded_base_type(declarator_type(d));
  return is_function_pointer_type (t);
}

Bool
is_function_call(expression me)
{
  expression e = me;
  if (expression_tag(e) == CallExpr_kind) {
    e = CallExpr_func (e);
  }
  if (expression_tag(e) != IdExpr_kind) return FALSE;
  declarator d = IdExpr_decl(e);
  if (!d) return TRUE; /* Implicit function call */
  Type t = declarator_type(d);
  return is_function_type (t);
}
